<?php
/**
 * XPHPFramework
 *
 * XRequest class, XArrayObject class, XArrayElementObject class, XCookieObject class,
 * XCookieElementObject class,XSessionObject class
 *
 * PHP version 5.3
 * 
 * @category XArrayObject
 * @package XPHPFramework
 * @author chopins xiao <chopins.xiao@gmail.com>
 * @copyright  2012 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @link       http://blog.toknot.com
 * @since      File available since Release 2.2
 */

exists_frame();

/**
 * XArrayObject element object class
 *
 * @category XArrayObject
 * @package XPHPFramework
 * @author chopins xiao <chopins.xiao@gmail.com>
 */
class XArrayElementObject {
    /**
     * save element value
     *
     * @var mixed
     * @access public
     */
    public $value;

    /**
     * save element string key
     *
     * @var string
     * @access public
     */
    public $skey;

    /**
     * save the element hash int key
     *
     * @var int
     * @access private 
     */
    private $int_index;

    /**
     * save the next element hash int key
     *
     * @var int
     * @access public-readonly
     */
    public $next;

    /**
     * save the previous element hash int key
     *
     * @var int
     * @access public-readonly
     */
    public $up;
    public function __construct($value) {
        $this->value = $value;
    }
    public function __toString() {
        return $this->value;
    }
}

/**
 * XFixedArrayObject
 */
class XFixedArrayObject implements Iterator,ArrayAccess,Countable {
    public $size = 0;
    public $data = null;
    private $start_index = null;
    private $end_index = null;
    private $current_int_key = 0;
    protected $elementObjectName = 'XArrayElementObject';
    public function __construct($array) {
        $this->size = count($array);
        $this->data = new SplFixedArray($this->size);
        $up = null;
        foreach($array as $key => $value) {
            $int_index = $this->getIndex($key);
            if($up !== null) {
                $this->data[$up]->next = $int_index;
            } else {
                $this->current_int_key = $int_index;
                $this->start_index = $int_index;
            }
            $this->data->offsetSet(0,$this->setElement($value));
            $this->data[$int_index] = $this->getElementObject($value);
            $this->data[$int_index]->skey = $key;
            $this->data[$int_index]->int_index = $int_index;
            $this->data[$int_index]->up = $up;
            $this->data[$int_index]->next = null;
            $up = $int_index;
        }
        if(isset($int_index)) {
            $this->end_index = $int_index;
        }
    }
    protected function setElement($value) {
        $ref = new ReflectionClass($this->elementObjectName);
        return $ref->newInstance($value);
    }
    public function getIndex($str_key) {
        $int_hash = 0;
        $key_len = strlen($str_key);
        for($i=0;$i<$key_len;$i++) {
            $int_hash = ((($int_hash <<5) + $int_hash) + ord($str_key[$i])) % 5813;
        }
        return $int_hash;
    }
    public function offsetExists($str_index) {
        $int_index = $this->getIndex($str_index);
        return isset($this->data[$int_index]);
    }
    public function offsetGet($str_index) {
        if($this->offsetExists($index)) {
            $int_index = $this->getIndex($str_index);
            return $this->data->offsetGet($int_index);
        }
        throw new XDebug("$index offset is not exists");
    }
    public function rewind() {
        $this->current_int_key = $this->start_index;
    }
    public function current() {
        return $this->data[$this->current_int_key];
    }
    public function key() {
        return $this->current_int_key;
    }
    public function next() {
        $this->current_int_key = $this->data[$this->current_int_key]->next;
    }
    public function valid() {
        if($this->current_int_key === null) return false;
        return isset($this->data[$this->current_int_key]);
    }
    public function count() {
        return $this->size;
    }
    public function offsetUnset($sKey) {
        $int_key = $this->getIndex($sKey);
        $current = $this->data->offsetGet($int_key);
        if($current->up !== null && $current->next !== null) {
            $this->data->offsetGet($current->up)->next = $current->next;
            $this->data->offsetGet($current->next)->up = $current->up;
        } else if($current->up !== null && $current->next === null) {
            $this->data->offsetGet($current->up)->next = null;
        } else if($current->next !== null && $current->up === null) {
            $this->data->offsetGet($current->next)->up = null;
        }
        $this->data->offsetUnset($int_key);
    }
    public function setKeyValue($sKey,$value) {
        $int_key = $this->getIndex($sKey);
        if($this->offsetExists($index)) {
            $this->data[$int_key]->value = $value;
        } else {
            $this->size++;
            $this->data->setSize($this->size);
            $this->data[$int_key] = new DataObject($value);
            $this->data[$int_key]->skey = $sKey;
            $this->data[$int_key]->int_index = $int_key;
            $this->data[$int_key]->up = $this->end_index;
            $this->data[$this->end_index]->next = $int_key;
            $this->data[$int_key]->next = null;
            $this->end_index = $int_index;
        }
    }
    public function offsetSet($sKey, $value) {
        $this->setKeyValue($sKey,$value);
    }
    public function __set($sKey, $value) {
        $this->setKeyValue($sKey,$value);
    }
    public function __get($sKey) {
        return $this->offsetGet($sKey);
    }
    public function __isset($sKey) {
        return $this->offsetExists($sKey);
    }
    public function __unset($sKey) {
        $this->offsetUnset($sKey);
    }
}
